/*
* Copyright (C) [2020] Futurewei Technologies, Inc. All rights reverved.
*
* OpenArkFE is licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
*
*  http://license.coscl.org.cn/MulanPSL
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
* FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v1 for more details.
*/
#include <vector>
#include <cstring>

#include "reserved_gen.h"
#include "spec_parser.h"
#include "auto_gen.h"
#include "base_gen.h"
#include "massert.h"

///////////////////////////////////////////////////////////////////////////////////////
//                       Debug functions during parsing
// During matching phase in the language parser, we often need to print the stack of
// rule table traversing in order to easily find the bug cause. So we need a set of
// functions to help dump the information. As an example, a major request is to dump the
// table name when we step into a new rule table. This request a mapping from the ruletable
// address to the table name.
//
// We generate two files gen_debug.h and gen_debug.cpp to include all the functions we
// may need. These two files are generated by autogen. Don't modify them.
//
// 1. For the rule table name mapping
//
//    In header file, we need
//
//      typedef struct {
//        const RuleTable *mAddr;
//        const char      *mName;
//      }RuleTableName;
//      extern RuleTableName gRuleTableNames[];
//      extern unsigned RuleTableNum;
//
//    In Cpp file, we need
//
//    #include "gen_debug.h"
//    unsigned RuleTableNum;
//    RuleTableName gRuleTableNames[] = {
//      {&TblLiteral, "TblLiteral"},
//      ...
//    };
///////////////////////////////////////////////////////////////////////////////////////

FileWriter *gDebugHFile;
FileWriter *gDebugCppFile;
unsigned    gRuleTableNum;

static void WriteDebugHFile() {
  gDebugHFile->WriteOneLine("#ifndef __DEBUG_GEN_H__", 23);
  gDebugHFile->WriteOneLine("#define __DEBUG_GEN_H__", 23);
  gDebugHFile->WriteOneLine("#include \"ruletable.h\"", 22);
  gDebugHFile->WriteOneLine("typedef struct {", 16);
  gDebugHFile->WriteOneLine("  const RuleTable *mAddr;", 25);
  gDebugHFile->WriteOneLine("  const char      *mName;", 25);
  gDebugHFile->WriteOneLine("}RuleTableName;", 15);
  gDebugHFile->WriteOneLine("extern RuleTableName gRuleTableNames[];", 39);
  gDebugHFile->WriteOneLine("extern unsigned RuleTableNum;", 29);
  gDebugHFile->WriteOneLine("extern const char* GetRuleTableName(const RuleTable*);", 54);
  gDebugHFile->WriteOneLine("#endif", 6);
}

// write the beginning part of debug file
static void PrepareDebugCppFile() {
  gDebugCppFile->WriteOneLine("#include \"gen_debug.h\"", 22);
  gDebugCppFile->WriteOneLine("#include \"common_header_autogen.h\"", 34);
  gDebugCppFile->WriteOneLine("RuleTableName gRuleTableNames[] = {", 35);
}

// write the ending part of debug file
static void FinishDebugCppFile() {
  gDebugCppFile->WriteOneLine("};", 2);
  std::string num = std::to_string(gRuleTableNum);
  std::string num_line = "unsigned RuleTableNum = ";
  num_line += num;
  num_line += ";";
  gDebugCppFile->WriteOneLine(num_line.c_str(), num_line.size());
  gDebugCppFile->WriteOneLine("const char* GetRuleTableName(const RuleTable* addr) {", 53);
  gDebugCppFile->WriteOneLine("  for (unsigned i = 0; i < RuleTableNum; i++) {", 47);
  gDebugCppFile->WriteOneLine("    RuleTableName name = gRuleTableNames[i];", 44);
  gDebugCppFile->WriteOneLine("    if (name.mAddr == addr)", 27);
  gDebugCppFile->WriteOneLine("      return name.mName;", 24);
  gDebugCppFile->WriteOneLine("  }", 3);
  gDebugCppFile->WriteOneLine("  return NULL;", 14);
  gDebugCppFile->WriteOneLine("}", 1);
}

void AutoGen::Init() {
  std::string lang_path_header("../../java/include/");
  std::string lang_path_cpp("../../java/src/");

  std::string debug_file_name = lang_path_cpp + "gen_debug.cpp";
  gDebugCppFile = new FileWriter(debug_file_name);
  debug_file_name = lang_path_header + "gen_debug.h";
  gDebugHFile = new FileWriter(debug_file_name);
  gRuleTableNum = 0;

  WriteDebugHFile();
  PrepareDebugCppFile();

  std::string hFile = lang_path_header + "gen_reserved.h";
  std::string cppFile = lang_path_cpp + "gen_reserved.cpp";
  mReservedGen = new ReservedGen("reserved.spec", hFile.c_str(), cppFile.c_str());
  mReservedGen->SetReserved(mReservedGen);
  mGenArray.push_back(mReservedGen);

  hFile = lang_path_header + "gen_iden.h";
  cppFile = lang_path_cpp + "gen_iden.cpp";
  mIdenGen  = new IdenGen("identifier.spec", hFile.c_str(), cppFile.c_str());
  mIdenGen->SetReserved(mReservedGen);
  mGenArray.push_back(mIdenGen);

  hFile = lang_path_header + "gen_literal.h";
  cppFile = lang_path_cpp + "gen_literal.cpp";
  mLitGen  = new LiteralGen("literal.spec", hFile.c_str(), cppFile.c_str());
  mLitGen->SetReserved(mReservedGen);
  mGenArray.push_back(mLitGen);

  hFile = lang_path_header + "gen_type.h";
  cppFile = lang_path_cpp + "gen_type.cpp";
  mTypeGen  = new TypeGen("type.spec", hFile.c_str(), cppFile.c_str());
  mTypeGen->SetReserved(mReservedGen);
  mGenArray.push_back(mTypeGen);

  hFile = lang_path_header + "gen_attr.h";
  cppFile = lang_path_cpp + "gen_attr.cpp";
  mAttrGen  = new AttrGen("attr.spec", hFile.c_str(), cppFile.c_str());
  mAttrGen->SetReserved(mReservedGen);
  mGenArray.push_back(mAttrGen);

  hFile = lang_path_header + "gen_block.h";
  cppFile = lang_path_cpp + "gen_block.cpp";
  mBlockGen  = new BlockGen("block.spec", hFile.c_str(), cppFile.c_str());
  mBlockGen->SetReserved(mReservedGen);
  mGenArray.push_back(mBlockGen);

  hFile = lang_path_header + "gen_separator.h";
  cppFile = lang_path_cpp + "gen_separator.cpp";
  mSeparatorGen  = new SeparatorGen("separator.spec", hFile.c_str(), cppFile.c_str());
  mSeparatorGen->SetReserved(mReservedGen);
  mGenArray.push_back(mSeparatorGen);

  hFile = lang_path_header + "gen_operator.h";
  cppFile = lang_path_cpp + "gen_operator.cpp";
  mOperatorGen  = new OperatorGen("operator.spec", hFile.c_str(), cppFile.c_str());
  mOperatorGen->SetReserved(mReservedGen);
  mGenArray.push_back(mOperatorGen);

  hFile = lang_path_header + "gen_keyword.h";
  cppFile = lang_path_cpp + "gen_keyword.cpp";
  mKeywordGen  = new KeywordGen("keyword.spec", hFile.c_str(), cppFile.c_str());
  mKeywordGen->SetReserved(mReservedGen);
  mGenArray.push_back(mKeywordGen);

  hFile = lang_path_header + "gen_expr.h";
  cppFile = lang_path_cpp + "gen_expr.cpp";
  mExprGen  = new ExprGen("expr.spec", hFile.c_str(), cppFile.c_str());
  mExprGen->SetReserved(mReservedGen);
  mGenArray.push_back(mExprGen);

  hFile = lang_path_header + "gen_stmt.h";
  cppFile = lang_path_cpp + "gen_stmt.cpp";
  mStmtGen  = new StmtGen("stmt.spec", hFile.c_str(), cppFile.c_str());
  mStmtGen->SetReserved(mReservedGen);
  mGenArray.push_back(mStmtGen);
}

AutoGen::~AutoGen() {
  if (mReservedGen)
    delete mReservedGen;
  if (mIdenGen)
    delete mIdenGen;
  if (mLitGen)
    delete mLitGen;
  if (mTypeGen)
    delete mTypeGen;
  if (mAttrGen)
    delete mAttrGen;
  if (mBlockGen)
    delete mBlockGen;
  if (mSeparatorGen)
    delete mSeparatorGen;
  if (mOperatorGen)
    delete mOperatorGen;
  if (mKeywordGen)
    delete mKeywordGen;
  if (mExprGen)
    delete mExprGen;
  if (mStmtGen)
    delete mStmtGen;
  if (gDebugHFile)
    delete gDebugHFile;
  if (gDebugCppFile)
    delete gDebugCppFile;
}

// When parsing a rule, its elements could be rules in the future rules, or
// it could be in another XxxGen. We simply put Pending for these elements.
//
// BackPatch() is the one coming back to solve these Pending. It has to do
// a traversal to solve one by one.
void AutoGen::BackPatch() {
  std::vector<BaseGen*>::iterator it = mGenArray.begin();
  //  std::cout << "mGenArray.size " << mGenArray.size() << std::endl;
  for (; it != mGenArray.end(); it++){
    BaseGen *gen = *it;
    gen->BackPatch(mGenArray);
  }
}

void AutoGen::Run() {
  mReservedGen->Run(mParser);
  mIdenGen->Run(mParser);
  mLitGen->Run(mParser);
  mTypeGen->Run(mParser);
  mAttrGen->Run(mParser);

  mBlockGen->Run(mParser);
  mSeparatorGen->Run(mParser);
  mOperatorGen->Run(mParser);
  mKeywordGen->Run(mParser);
  mExprGen->Run(mParser);
  mStmtGen->Run(mParser);
}

void AutoGen::Gen() {
  Init();
  Run();
  BackPatch();

  mReservedGen->Generate();
  mIdenGen->Generate();
  mLitGen->Generate();
  mTypeGen->Generate();
  mAttrGen->Generate();

  mBlockGen->Generate();
  mSeparatorGen->Generate();
  mOperatorGen->Generate();
  mKeywordGen->Generate();
  mExprGen->Generate();
  mStmtGen->Generate();

  FinishDebugCppFile();
}

